#include "GraphicsGL.hpp"
#include "VertexBufferGL.hpp"
#include "TextureGL.hpp"

namespace Graphics
{
	using namespace RevEng;
	using namespace RevEng::Math;

	GraphicsGL::GraphicsGL() : m_pVerts(NULL)
	{
		glfwInit();

		glfwOpenWindow(640, 480, 8, 8, 8, 8, 16, 0, GLFW_WINDOW);
		glfwDisable(GLFW_AUTO_POLL_EVENTS);

		glEnable(GL_DEPTH_TEST);
		glEnable(GL_TEXTURE_2D);
		//glEnable(GL_LIGHTING);

		glewInit();

		IObject::RegisterObjCreator( new ObjCreator<VertexBufferVBO, GR_VERTEX_BUFFER>);
		IObject::RegisterObjCreator( new ObjCreator<TextureGL, GR_TEXTURE>);
	}


	GraphicsGL::~GraphicsGL()
	{
		glfwTerminate();
	}


	void GraphicsGL::BeginFrame(const RevEng::Math::Vector4s &Color)
	{
		glClearColor(Color.X, Color.Y, Color.Z, Color.W);
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	}


	void GraphicsGL::EndFrame()
	{
		glFlush();
		glfwSwapBuffers();
	}
	

	void GraphicsGL::SetViewport(const RevEng::Math::Rect<RevEng::UInt32> &Area)
	{
		glViewport( Area.GetMinEdge().X, Area.GetMinEdge().Y, 
					Area.GetSize().X, Area.GetSize().Y);
	}


	void GraphicsGL::SetTransform(RevEng::Enum TransType, const RevEng::Math::Matrix4s &In)
	{
		switch (TransType)
		{
		case RE_WORLD:
			glMatrixMode(GL_MODELVIEW);
			glLoadMatrixf(m_MatView);
			glMultMatrixf(In);
			m_MatWorld = In;
			break;

		case RE_VIEW:
			glMatrixMode(GL_MODELVIEW);
			glLoadMatrixf(In);
			glMultMatrixf(m_MatWorld);
			m_MatView = In;
			break;

		case RE_PROJECTION:
			glMatrixMode(GL_PROJECTION);
			glLoadMatrixf(In);
			break;
		}
	}


	void GraphicsGL::SetLight(RevEng::UInt32 Index, const Light& Lgt)
	{

	}


	void GraphicsGL::SetMaterial(const Material& Mat)
	{
		glMaterialfv( GL_FRONT, GL_AMBIENT, Mat.Ambient );
		glMaterialfv( GL_FRONT, GL_DIFFUSE, Mat.Diffuse );
		glMaterialfv( GL_FRONT, GL_SPECULAR, Mat.Specular );
		glMaterialfv( GL_FRONT, GL_EMISSION, Mat.Emissive );
		glMaterialf( GL_FRONT, GL_SHININESS, Mat.Shininess );

		if ((ITexture*)Mat.Texture != NULL)
			glBindTexture( GL_TEXTURE_2D, dynamic_cast<TextureGL*>( (ITexture*)Mat.Texture )->GetId() );
	}


	void GraphicsGL::SetVertices(IVertexBuffer* Buffer)
	{
		VertexBufferVBO* VBO;

		m_pVerts = Buffer;
		VBO = (VertexBufferVBO*)Buffer;

		glBindBufferARB(GL_ARRAY_BUFFER_ARB, VBO->GetId());
		
		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);

		glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (GLvoid*)0);
		glNormalPointer(GL_FLOAT, sizeof(Vertex), (GLvoid*)12);
		glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid*)24);
	}


	void GraphicsGL::SetIndices(IIndexBuffer* Buffer)
	{
		
	}


	void GraphicsGL::Render(RevEng::Enum Primitive/*, RevEng::UInt32 Count*/)
	{
		GLenum Mode;

		switch (Primitive)
		{
			case RE_POINT_LIST:
				Mode = GL_POINTS;
				break;

			case RE_LINE_LIST:
				Mode = GL_LINES;
				break;

			case RE_LINE_LOOP:
				Mode = GL_LINE_LOOP;
				break;

			case RE_TRIANGLE_LIST:
				Mode = GL_TRIANGLES;
				break;

			case RE_TRIANGLE_STRIP:
				Mode = GL_TRIANGLE_STRIP;
				break;

			case RE_TRIANGLE_FAN:
				Mode = GL_TRIANGLE_FAN;
				break;
		}

		glDrawArrays(Mode, 0, m_pVerts->GetVertexCount());
	}


	bool GraphicsGL::DoEvents()
	{
		glfwPollEvents();

		if (glfwGetKey(GLFW_KEY_ESC) == GLFW_PRESS)
			return false;

		return true;
	}
}